package com.haltakov.letterjumble.ui.components;

import java.awt.AlphaComposite;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JComponent;

import com.haltakov.letterjumble.Messages;
import com.haltakov.letterjumble.game.api.Board;
import com.haltakov.letterjumble.game.api.BoardWord;
import com.haltakov.letterjumble.game.api.GameController;
import com.haltakov.letterjumble.game.api.Letter;
import com.haltakov.letterjumble.game.api.WordDirection;
import com.haltakov.letterjumble.ui.GraphicHelpers;

import static com.haltakov.letterjumble.ui.LookAndFeel.*;

@SuppressWarnings("serial")
public class GameBoard extends JComponent {
	private static final String WORD_X2_TEXT = Messages.getString("GameBoard.doubleword"); //$NON-NLS-1$
	private static final String WORD_X3_TEXT = Messages.getString("GameBoard.trippleword"); //$NON-NLS-1$
	private static final String LETTER_X2_TEXT = Messages.getString("GameBoard.doubleletter"); //$NON-NLS-1$
	private static final String LETTER_X3_TEXT = Messages.getString("GameBoard.trippleletter"); //$NON-NLS-1$
	private static final String START_TEXT = Messages.getString("GameBoard.start"); //$NON-NLS-1$
	
	private final Board board;
	private int xMargin, yMargin;
	private int boardWidth, boardHeight;
	private Image logo;
	private boolean drawLogo = false;
	private PlayerLetters playerLetters;	
	private final ArrayList<Letter> wordLetters;
	private GameController game;

	private void updateWordLetters() {
		wordLetters.clear();
		
		if (playerLetters != null)
			wordLetters.addAll(playerLetters.getWordLetters());
	}
	
	public GameBoard(final Board board, Image logo, PlayerLetters playerLetters, final GameController game) {
		this.board = board;
		this.logo = logo;
		this.playerLetters = playerLetters;
		this.game = game;
		
		wordLetters = new ArrayList<Letter>();
		
		boardWidth = board.getCountX()*SQUARE_SIZE + (board.getCountX()+1)*BORDER_SIZE;
		boardHeight = board.getCountY()*SQUARE_SIZE + (board.getCountY()+1)*BORDER_SIZE;
		
		addMouseListener(new MouseListener() {

			@Override
			public void mouseClicked(MouseEvent e) {
				if (e.getX() >= xMargin && e.getX() <= (xMargin+boardWidth) &&
					e.getY() >= yMargin && e.getY() <= (yMargin+boardHeight)) {
					
					int x = (e.getX()-xMargin)/(SQUARE_SIZE+BORDER_SIZE);
					int y = (e.getY()-yMargin)/(SQUARE_SIZE+BORDER_SIZE);
					
					if (game.getTempWord() != null && game.getTempWord().getX() == x && game.getTempWord().getY() == y) 
						game.setTempWord(null);
					else {
						WordDirection direction = null;
						int i = x, j = y;
						
						if (e.getButton() == MouseEvent.BUTTON1) 
							direction = WordDirection.HORIZONTAL;
						else if (e.getButton() == MouseEvent.BUTTON3) 
							direction = WordDirection.VERTICAL;
						else
							return;

						updateWordLetters();
						
						if (wordLetters.size() == 0)
							return;
						
						ArrayList<Letter> letters = new ArrayList<Letter>();
						
						for (int k = 0; k<wordLetters.size(); ) {
							if (i >= board.getCountX() || j >= board.getCountY()) {
								game.setTempWord(null);
								repaint();
								return;
							}
							
							Letter l = board.getLetter(i, j);
							if (l != null) {
								letters.add(l);
							}
							else {							
								letters.add(wordLetters.get(k));
								++k;
							}
							
							if (direction == WordDirection.HORIZONTAL)
								++i;
							else
								++j;
						}
						
						if (direction == WordDirection.HORIZONTAL) {
							for ( ; i<board.getCountX() && board.getLetter(i, j) != null; ++i)
								letters.add(board.getLetter(i, j));
						}
						else {
							for ( ; j<board.getCountY() && board.getLetter(i, j) != null; ++j)
								letters.add(board.getLetter(i, j));							
						}
						
						game.setTempWord(new BoardWord(board, letters, x, y, direction));
					}
				}
				repaint();
			}

			@Override
			public void mouseEntered(MouseEvent e) {
			}

			@Override
			public void mouseExited(MouseEvent e) {
			}

			@Override
			public void mousePressed(MouseEvent e) {
			}

			@Override
			public void mouseReleased(MouseEvent e) {
			}
			
		});
	}
	
	private void paintBackground(Graphics g) {
		if (drawLogo && logo != null) {
			g.drawImage(logo, xMargin + boardWidth/2 - logo.getWidth(null)/2, 20, null, null);
		}
	}
	
	private void paintBoard(Graphics g) {
		
		Map<Object, Object> m = new HashMap<Object, Object>();
		m.put(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		((Graphics2D) g).addRenderingHints(m);

		
		int delta = SQUARE_SIZE + BORDER_SIZE;
		
		g.setColor(DESK_BOARD_COLOR);
		g.fill3DRect(xMargin-5, yMargin-5, boardWidth+10, boardHeight+10, true);
		
		g.setColor(DESK_BORDER_COLOR);
		g.fillRect(xMargin, yMargin, boardWidth, boardHeight);
		
		g.setFont(BOARD_FONT);
		FontMetrics fm = g.getFontMetrics();
		
		int x = xMargin + BORDER_SIZE;
		
		for (int i=0; i<board.getCountX(); ++i) {
			int y = yMargin + BORDER_SIZE;
			
			for (int j=0; j<board.getCountY(); ++j) {

				switch (board.getCellType(i, j)) {
					case NORMAL: g.setColor(DESK_BOARD_COLOR); break;
					case LETTERx2: g.setColor(LETTER_X2_COLOR); break;
					case LETTERx3: g.setColor(LETTER_X3_COLOR); break;
					case WORDx2: g.setColor(WORD_X2_COLOR); break;
					case WORDx3: g.setColor(WORD_X3_COLOR); break;
					case START: g.setColor(START_COLOR); break;
				}
				
				g.fill3DRect(x, y, SQUARE_SIZE, SQUARE_SIZE, true);
				
				String text = ""; //$NON-NLS-1$
				
				g.setColor(TEXT_COLOR);
				switch (board.getCellType(i, j)) {
					case WORDx2: text = WORD_X2_TEXT; break;
					case WORDx3: text = WORD_X3_TEXT; break;
					case LETTERx2: text = LETTER_X2_TEXT; break;
					case LETTERx3: text = LETTER_X3_TEXT; break;
					case START: text = START_TEXT; break;
				}
				
				if (!text.isEmpty())
					GraphicHelpers.drawStringCentered(g, fm, text, x, y, SQUARE_SIZE, SQUARE_SIZE); 
				
				y += delta;
			}
			
			x += delta;
		}
	}

	private void drawLetter(Graphics g, Letter l, int x, int y, float transparency) {
		
		Graphics2D g2d = ((Graphics2D) g);
		Composite composite = null;
		
		if (transparency < 1f) {
			composite = g2d.getComposite();
			g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, transparency));
		}
		
		g2d.setColor(CELL_COLOR);
		g2d.fillRect(x, y, SQUARE_SIZE+2, SQUARE_SIZE+2);
		
		g2d.setColor(CELL_COLOR.brighter());
		g2d.fillRect(x, y, SQUARE_SIZE+2, 3);
		g2d.fillRect(x, y, 3, SQUARE_SIZE+2);
		
		g2d.setColor(CELL_COLOR.darker());
		g2d.fillRect(x, y + SQUARE_SIZE-1, SQUARE_SIZE+2, 3);
		g2d.fillRect(x + SQUARE_SIZE-1, y, 3, SQUARE_SIZE);
				    
		g2d.setColor(TEXT_COLOR);
		g2d.setFont(LETTER_FONT);
		FontMetrics fm = g2d.getFontMetrics();
		GraphicHelpers.drawStringCentered(g2d, fm, l.getCharacter().toString(), x, y, SQUARE_SIZE, SQUARE_SIZE);
		g2d.setFont(LETTER_POINTS_FONT);
		g2d.drawString("" + l.getPoints(), x+SQUARE_SIZE-7, y+SQUARE_SIZE-4); //$NON-NLS-1$
		
		if (transparency < 1f) {
			g2d.setComposite(composite);
		}
	}
	
	private void drawLetter(Graphics g, Letter l, int x, int y) {
		drawLetter(g, l, x, y, 1f);
	}
	
	private void paintLetters(Graphics g) {
		g.setFont(LETTER_FONT);
		
		for (int i=0; i<board.getCountX(); i++) {
			for (int j=0; j<board.getCountY(); j++) {
				Letter l = board.getLetter(i, j);
				
				if (l != null) {
					int x = xMargin + BORDER_SIZE*(i+1) + SQUARE_SIZE*i - 1;
					int y = yMargin + BORDER_SIZE*(j+1) + SQUARE_SIZE*j - 1;
							     
					drawLetter(g, l, x, y);
				}
			}
		}
		
		if (game.getTempWord() != null) {
			int dx = 0, dy = 0;
			
			if (game.getTempWord().getDirection() == WordDirection.HORIZONTAL)
				dx = 1;
			else
				dy = 1;
			
			int x = xMargin + BORDER_SIZE*(game.getTempWord().getX()+1) + SQUARE_SIZE*game.getTempWord().getX() - 1;
			int y = yMargin + BORDER_SIZE*(game.getTempWord().getY()+1) + SQUARE_SIZE*game.getTempWord().getY() - 1;
			
			for (Letter l: game.getTempWord().getLetters()) {
				drawLetter(g, l, x, y, 0.7f);
								
				x += dx*(SQUARE_SIZE + BORDER_SIZE);
				y += dy*(SQUARE_SIZE + BORDER_SIZE);
			}
		}
	}
	
	@Override
	public void paint(Graphics g) {
		Dimension d = getSize();
		
		xMargin = (d.width-boardWidth)/2;
		
		if (boardHeight + logo.getHeight(null) + 40 < d.height) {
			drawLogo = true;
			yMargin = 60 + logo.getHeight(null);
		}
		else {
			drawLogo = false;
			yMargin = 20;
		}		
		
		paintBackground(g);
		paintBoard(g);
		paintLetters(g);	
	}
}
